package com.acme.multithreadinglab.JCIPexample;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;

import com.acme.multithreadinglab.JCIPexample.CrawlerIndexerPlainThreads.FileCrawler;

public class CrawlerIndexerExecutor {

	static class FileCrawler implements Runnable {
		private final List<BlockingDeque<File>> fileQueue;
		private final FileFilter fileFilter;
		private final File root;
		private int queueIndex;
		private final ExecutorService crawlExecutor;

		public FileCrawler(List<BlockingDeque<File>> fileQueue,
				final FileFilter fileFilter, File root, ExecutorService crawlExecutor) {
			this.fileQueue = fileQueue;
			this.root = root;
			this.fileFilter = new FileFilter() {
				public boolean accept(File f) {
					return f.isDirectory() || fileFilter.accept(f);
				}
			};
			this.crawlExecutor=  crawlExecutor;
		}

		private boolean alreadyIndexed(File f) {
			return false;
		}

		public void run() {
			crawl(root);
		}

		private void crawl(File root) {
			File[] entries = root.listFiles(fileFilter);
			if (entries != null) {
				for (File entry : entries)
					if (entry.isDirectory())
						crawlExecutor.submit(new FileCrawler(fileQueue, fileFilter, entry, crawlExecutor));
					else if (!alreadyIndexed(entry)) {
						try {
							fileQueue.get(queueIndex).put(entry);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						queueIndex = (queueIndex + 1) % fileQueue.size();
					}
			}
		}
	}

	static class Indexer implements Runnable {
		private final List<BlockingDeque<File>> queue;
		private final int myIndex;
		private int processedFiles;
		public Indexer(List<BlockingDeque<File>> queue, final int index) {
			this.queue = queue;
			this.myIndex = index;
		}

		public void run() {
			try {
				while (true) {
					File file = null;
					int currentIndex = myIndex;
					while (file == null) {
						file = (currentIndex == myIndex) ? queue.get(myIndex).poll(0, TimeUnit.NANOSECONDS)
														 : queue.get(currentIndex).pollLast(0, TimeUnit.NANOSECONDS);
						currentIndex = (currentIndex + 1) % queue.size();
					}
					indexFile(file);
				}
			} catch (InterruptedException e) {
				while (!queue.get(myIndex).isEmpty()) {
					try {
						indexFile(queue.get(myIndex).poll());
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
				}
			} finally {
				System.out.println("Indexer thread " + myIndex + " finished: " + processedFiles);
			}
		}

		public void indexFile(File file) throws InterruptedException {
			System.out.println(file.toString());
			processedFiles++;
		};
	}

	private static final int BOUND = 10;
	private static final int N_CONSUMERS = Runtime.getRuntime().availableProcessors();

	public static void startIndexing(File[] roots) {
		List<BlockingDeque<File>> queueList = new ArrayList<BlockingDeque<File>>();
		List<Thread> indexerThreadList = new ArrayList<Thread>();
		
		ExecutorService crawlExecutor = Executors.newFixedThreadPool(32);
		ExecutorService indexExecutor = Executors.newFixedThreadPool(32);
		
		for (int i = 0; i < N_CONSUMERS; i++) {
			queueList.add(new LinkedBlockingDeque<File>(BOUND));
		}

		FileFilter filter = new FileFilter() {
			public boolean accept(File file) {
				return true;
			}
		};
		
		for (File root : roots) {
			crawlExecutor.submit(new FileCrawler(queueList, filter, root, crawlExecutor));
		}

		int queueIndex = 0;
		for (int i = 0; i < N_CONSUMERS; i++) {
			indexExecutor.submit(new Indexer(queueList, queueIndex++));
		}
//			while (!crawlExecutor.isTerminated()) {
//				System.out.println(queueList.toString());
//			};
//		System.out.println("Crawl finished");
//		indexExecutor.shutdownNow();
//		try {
//			crawlerLatch.await();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
//		for (Thread indexer: indexerThreadList) {
//			indexer.interrupt();
//		}
	}
	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		startIndexing(new File[] { new File("C:\\DB_0002")});
		System.out.println("Time spent: " + (System.currentTimeMillis()-start));
	}
}